package com.carsa.blackberry.domain.services.impl;

import java.io.IOException;
import java.util.Calendar;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

import com.carsa.blackberry.CarsaApp;
import com.carsa.blackberry.Dummy;
import com.carsa.blackberry.MainKeys;
import com.carsa.blackberry.domain.dto.ArticuloDTO;
import com.carsa.blackberry.domain.dto.EntregaDTO;
import com.carsa.blackberry.domain.dto.EntregasQueueDTO;
import com.carsa.blackberry.domain.dto.SecondReasonDTO;
import com.carsa.blackberry.domain.services.webServices.WebServiceManager;
import com.carsa.blackberry.domain.services.webServices.ZSdareEntrega_Service;
import com.carsa.blackberry.domain.services.webServices.impl.ZSdareEntrega.ZsdareEntrega;
import com.carsa.blackberry.domain.services.webServices.impl.ZSdareEntrega.ZsdareEntregat;
import com.carsa.blackberry.util.DateUtil;
import com.carsa.blackberry.util.Logger;
import com.carsa.blackberry.util.Util;
import com.carsa.blackberry.util.persistence.FileHandler;
import com.carsa.blackberry.util.persistence.FileHandlerImpl;

public class EntregasServiceImpl {

	private GenericServiceImpl _genericService = new GenericServiceImpl();
	private WebServiceManager _wsManager = new WebServiceManager();

	public int refreshEntregas() {
		Vector entregas = this.fetchEntregasFromWS((String) CarsaApp
				.getMemory().get(MainKeys.PARAMETER_LEGAJO_DRIVER));

		if (entregas == null)
			return -1;

		this.persistEntregasToFS(this.mergeEntregas(entregas));

		if (entregas.isEmpty())
			return 0;
		else
			return 1;
	}

	/**
	 * Unifica las entregas recibidas, junto con las que actualmente tiene en
	 * memoria.
	 * 
	 * @param entregasFromWS
	 */
	// TODO : Javadoc : Terminate this Javadoc.
	public EntregasQueueDTO mergeEntregas(Vector entregasFromWS) {
		EntregasQueueDTO entregasFromMemory = (EntregasQueueDTO) CarsaApp
				.getMemory().remove(MainKeys.PARAMETER_PENDING_QUEUE);

		entregasFromMemory.addAll(entregasFromWS, (String) CarsaApp.getMemory()
				.get(MainKeys.PARAMETER_LEGAJO_DRIVER));

		CarsaApp.getMemory().put(MainKeys.PARAMETER_PENDING_QUEUE,
				entregasFromMemory);

		return entregasFromMemory;
	}

	/**
	 * Graba las entregas que pasamos en engregas en el file system.
	 * 
	 * @param entregas
	 */
	private void persistEntregasToFS(EntregasQueueDTO entregas) {
		FileHandler fHandler = new FileHandlerImpl();
		fHandler.entablishConnection(MainKeys.FILE_PATH_ENTREGAS);

		EntregasQueueDTO queue = null;
		try {
			queue = new EntregasQueueDTO(fHandler.getFileData());
		} catch (IOException e) {
			Logger.logError(e);
		}

		String legajo = (String) CarsaApp.getMemory().get(
				MainKeys.PARAMETER_LEGAJO_DRIVER);

		queue.addAll(entregas.getEntregas(legajo), legajo);

		_genericService.exportObjectToPersistenceFile(queue);
	}

	public void markEntregaAsSuccess(EntregaDTO unaEntrega, Hashtable parameters) {
		this.setOutputArticlesFromInput(unaEntrega, (Date) parameters
				.get(MainKeys.PARAMETER_DATE_TIME_MOMENT));

		Enumeration articles = unaEntrega.getArticles().elements();
		while (articles.hasMoreElements()) {
			ArticuloDTO anArticle = (ArticuloDTO) articles.nextElement();

			anArticle.setLfdif(0);
			anArticle.setLfimg(1);
		}

		Logger
				.logInfo("Se persistira la entrega:\n"
						+ unaEntrega.toXMLString());
		this.persistRefreshedEntrega(unaEntrega);
	}

	/**
	 * @param unaEntrega
	 *            La {@link EntregaDTO} origen.
	 * @param moment
	 *            Un {@link EntregaDTO} con la fecha y hora seteadas. Asi com
	 *            tambien tiene seteado el ayudante en base al legajo cno el qeu
	 *            se logueo en la aplicacion.
	 * @return
	 */
	private void setOutputArticlesFromInput(EntregaDTO unaEntrega, Date moment) {
		Enumeration articles = unaEntrega.getArticles().elements();
		while (articles.hasMoreElements()) {
			ArticuloDTO anArticle = (ArticuloDTO) articles.nextElement();

			anArticle.setOutputFromInput();

			anArticle.setAyuda((String) CarsaApp.getMemory().get(
					MainKeys.PARAMETER_LEGAJO_AYUDANTE));

			anArticle.setFeare(DateUtil.exportAs(moment,
					DateUtil.EXPORT_TYPE_ABAP, DateUtil.EXPORT_DATE));
			anArticle.setHoare(DateUtil.exportAs(moment,
					DateUtil.EXPORT_TYPE_ABAP, DateUtil.EXPORT_TIME));
		}
	}

	/**
	 * Parsea un listado de articulos para entregar directamente como los
	 * devuelve el WS a un listado de {@link EntregaDTO} donde hay una entrega y
	 * N articulos. Efectua el filtro en base al numero de grupo de entrega y el
	 * numero de entrega.
	 * 
	 * @param articlesToDeliver
	 * @return Un {@link Vector} de {@link EntregaDTO} conteniendo N
	 *         {@link ArticuloDTO}s.
	 */
	public Vector parseArticlesToDeliver(Vector articlesToDeliver) {
		// Aca parseo las entregas y voy armando un objeto entrega con N
		// articulos (ZSdareAre)
		Vector ret = new Vector();

		Enumeration elements = articlesToDeliver.elements();
		while (elements.hasMoreElements()) {
			ZsdareEntrega unArticuloDeUnaEntrega = (ZsdareEntrega) elements
					.nextElement();
			ArticuloDTO anArticle = new ArticuloDTO(unArticuloDeUnaEntrega);

			// Si ya existe la entrega...
			if (this.exists(ret, anArticle.getSammg(), anArticle.getVbeln())) {
				EntregaDTO laQueExiste = this.get(ret, anArticle.getSammg(),
						anArticle.getVbeln());

				// TODO : Functionality : ver si esto es necesario. Deberian ser
				// punteros!
				ret.removeElement(laQueExiste);

				int cantidad = new Double(anArticle.getLfimg()).intValue();
				anArticle.setLfimg(1);
				for (int idx = 0; idx < cantidad; idx++)
					laQueExiste.addArticle(anArticle);

				ret.addElement(laQueExiste);
			} else {
				EntregaDTO unaEntregaNueva = new EntregaDTO();
				int cantidad = new Double(anArticle.getLfimg()).intValue();
				anArticle.setLfimg(1);
				for (int idx = 0; idx < cantidad; idx++)
					unaEntregaNueva.addArticle(anArticle);

				ret.addElement(unaEntregaNueva);
			}
		}

		return ret;
	}

	public EntregaDTO get(Vector aVector, String groupFilter,
			String entregaFilter) {
		Enumeration elements = aVector.elements();
		while (elements.hasMoreElements()) {
			EntregaDTO unaEntrega = (EntregaDTO) elements.nextElement();

			if (unaEntrega.getSammg().equals(groupFilter)
					&& unaEntrega.getVbeln().equals(entregaFilter))
				return unaEntrega;
		}

		return null;
	}

	public boolean exists(Vector aVector, String groupFilter,
			String entregaFilter) {
		Enumeration elements = aVector.elements();
		while (elements.hasMoreElements()) {
			EntregaDTO unaEntrega = (EntregaDTO) elements.nextElement();

			if (unaEntrega.getSammg().equals(groupFilter)
					&& unaEntrega.getVbeln().equals(entregaFilter))
				return true;
		}

		return false;
	}

	public Vector fetchEntregasFromWS(String legajo) {
		Vector parameters = new Vector();
		parameters.addElement(legajo);

		// ZsdareEntregat entregas = (ZsdareEntregat) _wsManager.call(new
		// ZSdareEntrega_Service(), parameters);// TODO : Dummy : Entregas
		ZsdareEntregat entregas = new ZsdareEntregat();// TODO : Dummy :
														// Entregas

		if (entregas != null) {
			Vector entregasVector = Util.getVectorFrom(entregas.getItem());
			entregasVector = Dummy.getEntregas(); // TODO : Dummy : Entregas

			return this.parseArticlesToDeliver(entregasVector);
		}

		return null;
	}

	public boolean persistRefreshedEntrega(EntregaDTO unaEntrega) {
		boolean returnValue = true;

		_genericService.refreshPersistenceFile(unaEntrega);

		Enumeration articles = unaEntrega.getArticles().elements();
		while (articles.hasMoreElements()) {
			Vector parameters = new Vector();
			ArticuloDTO anArticle = ((ArticuloDTO) articles.nextElement());

			parameters.addElement(anArticle.getOutputWSObject());

			_genericService.pushMessageIntoQueue(anArticle);
		}

		unaEntrega.setCalified(true);
		_genericService.pushEntregaInHistory(unaEntrega);

		return returnValue;
	}

	public void markEntregaAsFail(EntregaDTO unaEntrega, Hashtable parameters,
			boolean isTotalFail) {
		if (!isTotalFail)
			return;

		setOutputArticlesFromInput(unaEntrega, (Date) parameters
				.get(MainKeys.PARAMETER_DATE_TIME_MOMENT));

		String reasonCode = (String) parameters.get(MainKeys.PARAMETER_REASON);
		String secondReasonCode = (String) parameters
				.get(MainKeys.PARAMETER_SECOND_REASON);

		// Da igual si tiene uno o N articulos porque se procesan de a uno.
		Enumeration elements = unaEntrega.getArticles().elements();
		while (elements.hasMoreElements()) {
			ArticuloDTO eachArticle = (ArticuloDTO) elements.nextElement();

			eachArticle.setGrund(reasonCode);
			eachArticle.setSubmo(secondReasonCode);
			eachArticle.setLfdif(1);
			eachArticle.setLfimg(1);

			Logger.logInfo("Se marco el articulo:\n" + eachArticle);
		}

		Logger
				.logInfo("Se persistira la entrega:\n"
						+ unaEntrega.toXMLString());
		this.persistRefreshedEntrega(unaEntrega);
	}

	public void markPartialFailEntregaAs(EntregaDTO unaEntrega,
			Hashtable parameters) {
		setOutputArticlesFromInput(unaEntrega, (Date) parameters
				.get(MainKeys.PARAMETER_DATE_TIME_MOMENT));

		Hashtable secondReasonsTable = (Hashtable) parameters
				.get(MainKeys.PARAMETER_ARTICLES_REASONS);

		// Da igual si tiene uno o N articulos porque se procesan de a uno.
		Enumeration elements = unaEntrega.getArticles().elements();
		for (int idx = 0; elements.hasMoreElements(); idx++) {
			ArticuloDTO eachArticle = (ArticuloDTO) elements.nextElement();

			SecondReasonDTO secondReasonDTO = (SecondReasonDTO) secondReasonsTable
					.get(new Integer(idx));
			eachArticle.setGrund(secondReasonDTO.getReasonCode());
			eachArticle.setSubmo(secondReasonDTO.getCode());

			// Si las razones son una cadena vacia, entonces debe ser calificada
			// como exitosa.
			// Sin embargo aparecera en el listado de fallidas.
			int value = (secondReasonDTO.getReasonCode().equals("") && secondReasonDTO
					.getCode().equals("")) ? 0 : 1;
			eachArticle.setLfdif(value);
			eachArticle.setLfimg(1);

			Logger.logInfo("Se marco el articulo:\n" + eachArticle);
		}

		Logger
				.logInfo("Se persistira la entrega:\n"
						+ unaEntrega.toXMLString());
		this.persistRefreshedEntrega(unaEntrega);
	}

	public EntregasQueueDTO getEntregasQueue() {
		String legajo = (String) CarsaApp.getMemory().get(
				MainKeys.PARAMETER_LEGAJO_DRIVER);

		EntregasQueueDTO queue = this.fetchEntregas(legajo);

		return queue;
	}

	/**
	 * @return
	 */
	// TODO : Javadoc : Terminate this Javadoc.
	public Vector getEntregasPendientes() {
		Vector ret = new Vector();

		String legajo = (String) CarsaApp.getMemory().get(
				MainKeys.PARAMETER_LEGAJO_DRIVER);

		EntregasQueueDTO queue = (EntregasQueueDTO) CarsaApp.getMemory().get(
				MainKeys.PARAMETER_PENDING_QUEUE);
		Enumeration elements = queue.getEntregas(legajo).elements();
		while (elements.hasMoreElements()) {
			EntregaDTO unaEntrega = (EntregaDTO) elements.nextElement();
			if (unaEntrega.isPending())
				ret.addElement(unaEntrega);
		}

		if (ret.isEmpty()) {
			queue.resetEntregas(legajo);
			_genericService.exportObjectToPersistenceFile(queue);

			EntregasQueueDTO refreshedQueue = this.fetchEntregas(legajo);

			CarsaApp.getMemory().put(MainKeys.PARAMETER_PENDING_QUEUE,
					refreshedQueue);

			elements = refreshedQueue.getEntregas(legajo).elements();
			while (elements.hasMoreElements()) {
				EntregaDTO unaEntrega = (EntregaDTO) elements.nextElement();
				if (unaEntrega.isPending())
					ret.addElement(unaEntrega);
			}
		}

		return ret;
	}

	/**
	 * @return
	 */
	// TODO : Javadoc : Terminate this Javadoc.
	public Vector getEntregasCalified() {
		Vector ret = new Vector();

		EntregasQueueDTO queue = (EntregasQueueDTO) CarsaApp.getMemory().get(
				MainKeys.PARAMETER_ENTREGAS_HISTORY);
		Enumeration elements = queue.getEntregas(
				(String) CarsaApp.getMemory().get(
						MainKeys.PARAMETER_LEGAJO_DRIVER)).elements();
		while (elements.hasMoreElements()) {
			EntregaDTO unaEntrega = (EntregaDTO) elements.nextElement();
			// if (!unaEntrega.isPending())// TODO : Functionality : check this!
			// en este archivo NO hay pendientes!
			ret.addElement(unaEntrega);
		}

		return ret;
	}

	public EntregasQueueDTO fetchEntregas(String legajo) {
		EntregasQueueDTO entregasQueue = new EntregasQueueDTO(false);

		FileHandler fHandler = new FileHandlerImpl();
		fHandler.entablishConnection(entregasQueue.getPersistPath());
		String fileData = null;
		try {
			fileData = fHandler.getFileData();
		} catch (IOException e) {
			Logger.logError(Logger.getLocationMessageFor(this,
					"fetchEntregas()"), e);
		}
		Logger.logInfo("fileData: " + fileData);

		if (fileData != null && !fileData.equals(""))
			entregasQueue.initializeFromXML(fileData);

		if (!entregasQueue.hasPendingEntregas(legajo)) {
			Vector entregasFromWS = this.fetchEntregasFromWS(legajo);

			if (entregasFromWS == null)
				entregasFromWS = new Vector();

			boolean flag;
			if (!entregasFromWS.isEmpty())
				flag = true;
			else
				flag = false;
			CarsaApp.getMemory()
					.put(MainKeys.PARAMETER_FLAG, new Boolean(flag));

			entregasQueue.addAll(entregasFromWS, legajo);

			_genericService.exportObjectToPersistenceFile(entregasQueue);
		}

		return entregasQueue;
	}

	public EntregasQueueDTO getEntregasHistory() {
		EntregasQueueDTO entregasQueue = new EntregasQueueDTO(true);

		FileHandler fHandler = new FileHandlerImpl();
		fHandler.entablishConnection(entregasQueue.getPersistPath());
		String fileData = null;
		try {
			fileData = fHandler.getFileData();
		} catch (IOException e) {
			Logger.logError(Logger.getLocationMessageFor(this,
					"getEntregasHistory()"), e);
		}
		Logger.logInfo("fileData: " + fileData);

		if (fileData != null && !fileData.equals(""))
			entregasQueue.initializeFromXML(fileData);

		if (this.hasToOverride(entregasQueue))
			entregasQueue = new EntregasQueueDTO(true);

		return entregasQueue;
	}

	public boolean hasToOverride(EntregasQueueDTO queue) {
		Date lastOverwrite = queue.getLastOverwrite();
		if (lastOverwrite == null)
			return true;

		int daysBetween = DateUtil.daysBetween(lastOverwrite, Calendar
				.getInstance().getTime());
		return daysBetween >= 1;
	}
}
